Skip to content

2776. 转换回调函数为 Promise 函数

2776. 转换回调函数为 Promise 函数

ts
type CallbackFn = (
  next: (data: number, error: string) => void,
  ...args: number[]
) => void
type Promisified = (...args: number[]) => Promise<number>

function promisify(fn: CallbackFn): Promisified {
  return (...args) =>
    new Promise((res, rej) => {
      fn((data, err) => {
        if (err) rej(err)
        res(data)
      }, ...args)
    })
}
/**
 * const asyncFunc = promisify(callback => callback(42));
 * asyncFunc().then(console.log); // 42
 */

Node.js 的回调约定

在 Promise 成为标准之前,Node.js 生态采用 (err, result) 回调约定——回调的第一个参数是错误,第二个是结果。promisify 就是这种约定到 Promise 的桥梁。

Node.js 内置的 util.promisify 做了完全相同的事,而且处理了更多边界情况(如回调有多个参数时返回对象)。

实现细节

这个实现执行 fn 时传入一个包装后的回调函数。当回调被调用时,检查 err 参数:

  • 有 err → reject
  • 无 err → resolve(data)

注意 fn 的签名是 (callback, ...args),所以包装后的参数 ...args 会在回调之后传入——这和 Node.js 常见的 (arg1, arg2, callback) 模式有区别。实际使用中需要确认函数签名匹配。

一个已知局限

如果 fn 的回调被多次调用(某些库的行为),Promise 只响应第一次——后续的 resolve/reject 调用会被忽略。这是 Promise 的单次决议特性,也是 promisify 的隐含契约:回调应该只被调用一次。